// Copyright (c) Nate McMaster.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

#if NETSTANDARD2_0
using IHostEnvironment = Microsoft.Extensions.Hosting.IHostingEnvironment;
#endif

namespace LettuceEncrypt.Internal
{
    /// <summary>
    /// Loads the ASP.NET Developer certificate
    /// /// </summary>
    internal class DeveloperCertLoader : ICertificateSource
    {
        // see https://github.com/aspnet/Common/blob/61320f4ecc1a7b60e76ca8fe05cd86c98778f92c/shared/Microsoft.AspNetCore.Certificates.Generation.Sources/CertificateManager.cs#L19-L20
        // This is the unique OID for the developer cert generated by VS and the .NET Core CLI
        private const string AspNetHttpsOid = "1.3.6.1.4.1.311.84.1.1";
        private const string AspNetHttpsOidFriendlyName = "ASP.NET Core HTTPS development certificate";
        private readonly IHostEnvironment _environment;
        private readonly ILogger<DeveloperCertLoader> _logger;

        public DeveloperCertLoader(
            IHostEnvironment environment,
            ILogger<DeveloperCertLoader> logger)
        {
            _environment = environment;
            _logger = logger;
        }

        public Task<IEnumerable<X509Certificate2>> GetCertificatesAsync(CancellationToken cancellationToken)
        {
            if (!_environment.IsDevelopment())
            {
                return Task.FromResult(Enumerable.Empty<X509Certificate2>());
            }

            var certs = FindDeveloperCert();

            return Task.FromResult(certs);
        }

        private IEnumerable<X509Certificate2> FindDeveloperCert()
        {
            using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
            store.Open(OpenFlags.ReadOnly);
            var certs = store.Certificates.Find(X509FindType.FindByExtension, AspNetHttpsOid, validOnly: false);
            if (certs.Count == 0)
            {
                _logger.LogDebug("Could not find the " + AspNetHttpsOidFriendlyName);
            }
            else
            {
                _logger.LogDebug("Using the " + AspNetHttpsOidFriendlyName + " for 'localhost' requests");

                foreach (var cert in certs)
                {
                    yield return cert;
                }
            }
        }
    }
}
